#pragma once

#include "const.h"
#include "Singleton.h"
#include "ConfigManager.h"
#include "message.grpc.pb.h"

#include <grpc++/grpc++.h>
#include <queue>
#include <string>

class StatusConnectPool
{
public:
	StatusConnectPool(std::string host, std::string port, size_t pool_size);
	~StatusConnectPool();

	std::unique_ptr<message::StatusService::Stub> getConnection();
	void returnConnection(std::unique_ptr<message::StatusService::Stub>  connection);
	void Close();

private:
	std::string m_host;
	std::string m_port;
	size_t m_pool_size;
	std::atomic_bool m_b_stop;
	std::mutex m_mutex;
	std::condition_variable m_cond;
	std::queue<std::unique_ptr<message::StatusService::Stub>> m_connections;
};

class StatusGrpcClient : public Singleton<StatusGrpcClient>
{
	friend class Singleton<StatusGrpcClient>;

public:
	~StatusGrpcClient() {

	}

	message::GetChatServerRsp getChatServer(int uid) {
		grpc::ClientContext context;
		message::GetChatServerRsp reply;
		message::GetChatServerReq request;
		request.set_uid(uid);

		// 从GRPCConPool取出一个连接
		auto stub = m_pool->getConnection();
		grpc::Status status = stub->GetChatServer(&context, request, &reply);
		Defer defer([&stub, this]() {
			m_pool->returnConnection(std::move(stub));
		});
		if (status.ok()) {
			return reply;
		}
		else {
			reply.set_error(ErrorCodes::RPCFailed);
			return reply;
		}
	}

	message::LoginRsp login(int uid, std::string token) {
		grpc::ClientContext context;
		message::LoginRsp reply;
		message::LoginReq request;
		request.set_uid(uid);
		request.set_token(token);

		auto stub = m_pool->getConnection();
		grpc::Status status = stub->Login(&context, request, &reply);
		Defer defer([&stub, this]() {
			m_pool->returnConnection(std::move(stub));
		});

		if (status.ok()) {
			return reply;
		}
		else {
			reply.set_error(ErrorCodes::RPCFailed);
			return reply;
		}
	}

private:
	StatusGrpcClient() {
		auto& gCfgMgr = ConfigManager::getInstance();
		std::string host = gCfgMgr["StatusServer"]["Host"];
		std::string port = gCfgMgr["StatusServer"]["Port"];
		m_pool.reset(new StatusConnectPool(host, port, 5));
	}

private:
	std::unique_ptr<StatusConnectPool> m_pool;
};

